<script>
import ajax from "./ajax";
import UploadDragger from "./upload-dragger.vue";
import SparkMD5 from "spark-md5";
function getFileMD5(file) {
    return new Promise((resolve, reject) => {
        const spark = new SparkMD5.ArrayBuffer();
        const fileReader = new FileReader();
        fileReader.onload = (e) => {
            spark.append(e.target.result);
            resolve(spark.end());
        };
        fileReader.onerror = () => {
            reject("");
        };
        fileReader.readAsArrayBuffer(file);
    });
}

export default {
    inject: ["uploader"],
    components: {
        UploadDragger,
    },
    props: {
        type: String,
        action: {
            type: String,
            required: true,
        },
        name: {
            type: String,
            default: "file",
        },
        data: Function,
        chunkSize: Number,
        thread: Number,
        headers: Object,
        withCredentials: Boolean,
        multiple: Boolean,
        accept: String,
        onStart: Function,
        onProgress: Function,
        onSuccess: Function,
        onError: Function,
        beforeUpload: Function,
        drag: Boolean,
        onPreview: {
            type: Function,
            default: function () {},
        },
        onRemove: {
            type: Function,
            default: function () {},
        },
        fileList: Array,
        autoUpload: Boolean,
        listType: String,
        httpRequest: {
            type: Function,
            default: ajax,
        },
        disabled: Boolean,
        limit: Number,
        onExceed: Function,
    },

    data() {
        return {
            mouseover: false,
            reqs: {},
        };
    },

    methods: {
        isImage(str) {
            return str.indexOf("image") !== -1;
        },
        handleChange(ev) {
            const files = ev.target.files;

            if (!files) return;
            this.uploadFiles(files);
        },
        uploadFiles(files) {
            if (
                this.limit &&
                this.fileList.length + files.length > this.limit
            ) {
                this.onExceed && this.onExceed(files, this.fileList);
                return;
            }

            let postFiles = Array.prototype.slice.call(files);
            if (!this.multiple) {
                postFiles = postFiles.slice(0, 1);
            }

            if (postFiles.length === 0) {
                return;
            }

            postFiles.forEach((rawFile) => {
                this.onStart(rawFile);
                if (this.autoUpload) this.upload(rawFile);
            });
        },
        upload(rawFile) {
            this.$refs.input.value = null;

            if (!this.beforeUpload) {
                return this.post(rawFile);
            }

            const before = this.beforeUpload(rawFile);
            if (before && before.then) {
                before.then(
                    (processedFile) => {
                        const fileType =
                            Object.prototype.toString.call(processedFile);

                        if (
                            fileType === "[object File]" ||
                            fileType === "[object Blob]"
                        ) {
                            if (fileType === "[object Blob]") {
                                processedFile = new File(
                                    [processedFile],
                                    rawFile.name,
                                    {
                                        type: rawFile.type,
                                    }
                                );
                            }
                            for (const p in rawFile) {
                                if (rawFile.hasOwnProperty(p)) {
                                    processedFile[p] = rawFile[p];
                                }
                            }
                            this.post(processedFile);
                        } else {
                            this.post(rawFile);
                        }
                    },
                    () => {
                        this.onRemove(null, rawFile);
                    }
                );
            } else if (before !== false) {
                this.post(rawFile);
            } else {
                this.onRemove(null, rawFile);
            }
        },
        abort(file) {
            const { reqs } = this;
            if (file) {
                let uid = file;
                if (file.uid) uid = file.uid;
                if (reqs[uid]) {
                    //reqs[uid].abort();
                    reqs[uid].forEach((item) => {
                        item.abort();
                    });
                }
            } else {
                Object.keys(reqs).forEach((uid) => {
                    if (reqs[uid]) {
                        //reqs[uid].abort();
                        reqs[uid].forEach(item=>{
                            item.abort();
                        })
                    }
                    delete reqs[uid];
                });
            }
        },
        async post(rawFile) {
            let fileHash = await getFileMD5(rawFile);
            let chunkList = [];

            for (let i = 0; i < rawFile.size; i = i + this.chunkSize) {
                const tmp = rawFile.slice(
                    I,
                    Math.min(i + this.chunkSize, rawFile.size)
                );
                chunkList.push(tmp);
            }
            const chunkHashList = await Promise.all(
                chunkList.map(async (item) => {
                    return await getFileMD5(item);
                })
            );
            const { uid } = rawFile;
            const options = {
                headers: this.headers,
                withCredentials: this.withCredentials,
                file: rawFile,
                data: this.data,
                filename: this.name,
                action: this.action,
                fileHash: fileHash,
                chunkList: chunkList,
                chunkHashList: chunkHashList,
                chunkSize: this.chunkSize,
                thread: this.thread,
                onProgress: (e) => {
                    this.onProgress(e, rawFile);
                },
                onSuccess: (res) => {
                    this.onSuccess(res, rawFile);
                    delete this.reqs[uid];
                },
                onError: (err) => {
                    this.onError(err, rawFile);
                    delete this.reqs[uid];
                },
            };
            const req = this.httpRequest(options);
            this.reqs[uid] = req;
            if (req && req.then) {
                req.then(options.onSuccess, options.onError);
            }
        },
        handleClick() {
            if (!this.disabled) {
                this.$refs.input.value = null;
                this.$refs.input.click();
            }
        },
        handleKeydown(e) {
            if (e.target !== e.currentTarget) return;
            if (e.keyCode === 13 || e.keyCode === 32) {
                this.handleClick();
            }
        },
    },

    render(h) {
        let {
            handleClick,
            drag,
            name,
            handleChange,
            multiple,
            accept,
            listType,
            uploadFiles,
            disabled,
            handleKeydown,
        } = this;
        const data = {
            class: {
                "el-upload": true,
            },
            on: {
                click: handleClick,
                keydown: handleKeydown,
            },
        };
        data.class[`el-upload--${listType}`] = true;
        return (
            <div {...data} tabindex="0">
                {drag ? (
                    <upload-dragger disabled={disabled} on-file={uploadFiles}>
                        {this.$slots.default}
                    </upload-dragger>
                ) : (
                    this.$slots.default
                )}
                <input
                    class="el-upload__input"
                    type="file"
                    ref="input"
                    name={name}
                    on-change={handleChange}
                    multiple={multiple}
                    accept={accept}
                ></input>
            </div>
        );
    },
};
</script>
